package cn.jane.rpc.netty.serialize.impl;

import static cn.jane.rpc.netty.util.NumberUtil.toByteSafely;
import static cn.jane.rpc.netty.util.NumberUtil.toShortSafely;

import cn.jane.rpc.netty.nameservice.Metadata;
import cn.jane.rpc.netty.serialize.Serializer;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;

/**
 * {@link cn.jane.rpc.netty.nameservice.Metadata} 的序列化器
 */
public class MetadataSerializer implements Serializer<Metadata> {

    @Override
    public int size(Metadata entry) {
        /*
        文件数据格式如下所示：
            DATA Metadata {
                short                   metadataCount       2 bytes     元数据个数
                MULTI<MetadataContent>  metadataContentList n bytes     元数据列表
            }
            DATA MetadataContent {
                byte                    keyLength           1 byte      元数据 key 的长度
                string                  key                 n bytes     元数据 key
                short                   uriSetCount         2 byte      元数据中保存的 URI 的个数
                MULTI<URIItem>          uriSet              n bytes     元数据中保存的 URI 列表
            }
            DATA URIItem {
                byte                    uriLength           1 byte      URI 的长度
                string                  uri                 n bytes     URI 内容
            }
         */
        return Short.BYTES +
                entry.entrySet().stream().mapToInt(this::calMetadataContentSize).sum();
    }

    // 计算单个 metadataContent 的大小
    private int calMetadataContentSize(Entry<String, Set<URI>> metadataContent) {
        return Byte.BYTES +
                metadataContent.getKey().getBytes().length +
                Short.BYTES +
                metadataContent.getValue().stream().mapToInt(uri ->
                        Byte.BYTES + uri.toASCIIString().getBytes(StandardCharsets.UTF_8).length
                ).sum();
    }

    @Override
    public void serialize(Metadata entry, byte[] bytes, int offset, int length) {
        ByteBuf buf = Unpooled.wrappedBuffer(bytes, offset, length);

        buf.writeShort(toShortSafely(entry.size()));
        entry.forEach((key, uriSet) -> {
            byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
            buf.writeByte(toByteSafely(keyBytes.length));
            buf.writeBytes(keyBytes);

            buf.writeShort(toShortSafely(uriSet.size()));
            uriSet.forEach(uri -> {
                byte[] uriBytes = uri.toASCIIString().getBytes(StandardCharsets.UTF_8);
                buf.writeByte(toByteSafely(uriBytes.length));
                buf.writeBytes(uriBytes);
            });
        });
    }

    @Override
    public Metadata parse(byte[] bytes, int offset, int length) {
        ByteBuf buf = Unpooled.wrappedBuffer(bytes, offset, length);
        Metadata metadata = new Metadata();

        int metadataCount = buf.readShort();
        for (int i = 0; i < metadataCount; i++) {
            int keyLength = buf.readByte();
            byte[] keyBytes = new byte[keyLength];
            buf.readBytes(keyBytes);
            String key = new String(keyBytes);

            int uriSetCount = buf.readShort();
            Set<URI> uriSet = new HashSet<>(uriSetCount);
            for (int j = 0; j < uriSetCount; j++) {
                int uriLength = buf.readByte();
                byte[] uriBytes = new byte[uriLength];
                buf.readBytes(uriBytes);
                uriSet.add(URI.create(new String(uriBytes, StandardCharsets.UTF_8)));
            }
            metadata.put(key, uriSet);
        }

        return metadata;
    }

    @Override
    public byte type() {
        return Types.METADATA;
    }

    @Override
    public Class<Metadata> getSerializeClass() {
        return Metadata.class;
    }
}
